﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;

namespace Xceed.Silverlight.Data
{
  internal class ItemChangedInfo
  {
    internal ItemChangedInfo()
    {
    }

    public ItemChangedInfo(
      object target )
    {
      if( target == null )
        throw new ArgumentNullException( "target" );

      this.Target = target;
    }

    public ItemChangedInfo(
      object target,
      string propertyName )
    {
      if( target == null )
        throw new ArgumentNullException( "target" );

      if( propertyName == null )
        throw new ArgumentNullException( "propertyName" );

      this.Target = target;
      m_modifiedProperties.Add( propertyName, new Tuple<bool, object>( false, null ) );
    }

    public ItemChangedInfo(
      object target,
      string propertyName,
      object oldValue )
    {
      if( target == null )
        throw new ArgumentNullException( "target" );

      if( propertyName == null )
        throw new ArgumentNullException( "propertyName" );

      this.Target = target;
      m_modifiedProperties.Add( propertyName, new Tuple<bool, object>( true, oldValue ) );
    }

    public ItemChangedInfo( object target, IEnumerable<string> properties )
    {
      if( target == null )
        throw new ArgumentNullException( "target" );

      if( properties == null )
        throw new ArgumentNullException( "properties" );

      if( !properties.Any() )
        throw new ArgumentException( "The specified properties must contain at least one entry.", "properties" );

      if( properties.Any( ( prop ) => prop == null ) )
        throw new ArgumentException( "The specified properties cannot contain null property names.", "properties" );

      this.Target = target;
      m_modifiedProperties = new Dictionary<string, Tuple<bool, object>>();

      foreach( string property in properties )
      {
        m_modifiedProperties.Add( property, new Tuple<bool, object>( false, null ) );
      }
    }

    public ItemChangedInfo( object target, IEnumerable<string> properties, IDictionary<string, object> availableOldValues )
    {
      if( target == null )
        throw new ArgumentNullException( "target" );

      if( properties == null )
        throw new ArgumentNullException( "properties" );

      if( availableOldValues == null )
        throw new ArgumentNullException( "availableOldValues" );

      if( !properties.Any() )
        throw new ArgumentException( "The specified properties must contain at least one entry.", "properties" );

      if( properties.Any( ( prop ) => prop == null ) )
        throw new ArgumentException( "The specified properties cannot contain null property names.", "properties" );

      this.Target = target;
      m_modifiedProperties = new Dictionary<string, Tuple<bool, object>>();

      foreach( string property in properties )
      {
        object oldValue = null;
        bool hasOldValue = availableOldValues.TryGetValue( property, out oldValue );

        m_modifiedProperties.Add( property, new Tuple<bool, object>( hasOldValue, oldValue ) );
      }
    }

    internal object Target
    {
      get;
      private set;
    }

    internal bool GetIsValueChanged( SortDescription sortDescription )
    {
      if( sortDescription == null )
        throw new ArgumentNullException( "sortDescription" );

      //Handle the "target-only" case
      if( m_modifiedProperties.Count == 0 )
      {
        //Assume that all properties have changed.
        return true;
      }

      return m_modifiedProperties.ContainsKey( sortDescription.PropertyName );
    }

    internal bool GetIsValueChanged( GroupDescription groupDescription )
    {
      if( groupDescription == null )
        throw new ArgumentNullException( "groupDescription" );

      //Handle the "target-only" case
      if( m_modifiedProperties.Count == 0 )
        //Assume that all properties have changed.
        return true;

      //Handle the unknown property. Assume changed.
      if( !( groupDescription is PropertyGroupDescription ) && !( groupDescription is DataGroupDescription ) )
        //Assume that the group value has changed.
        return true;

      return m_modifiedProperties.ContainsKey( groupDescription.GetPropertyName() );
    }

    internal bool TryGetOldValue( SortDescription sortDescription, out object oldValue )
    {
      if( sortDescription == null )
        throw new ArgumentNullException( "sortDescription" );

      Tuple<bool, object> oldInfo;
      if( m_modifiedProperties.TryGetValue( sortDescription.PropertyName, out oldInfo ) )
      {
        oldValue = oldInfo.Item2;
        return oldInfo.Item1;
      }
      else
      {
        oldValue = null;
        return false;
      }
    }

    internal bool TryGetOldValue( GroupDescription groupDescription, out object oldValue )
    {
      if( groupDescription == null )
        throw new ArgumentNullException( "groupDescription" );

      // Handle unknown property name
      if( !( groupDescription is PropertyGroupDescription ) && !( groupDescription is DataGroupDescription ) )
      {
        oldValue = null;
        return false;
      }

      Tuple<bool, object> oldInfo;

      if( m_modifiedProperties.TryGetValue( groupDescription.GetPropertyName(), out oldInfo ) )
      {
        oldValue = oldInfo.Item2;
        return oldInfo.Item1;
      }
      else
      {
        oldValue = null;
        return false;
      }
    }

    internal ItemChangedInfo CreateWithNewTarget( object newTarget )
    {
      if( newTarget == null )
        throw new ArgumentNullException( "newTarget" );

      ItemChangedInfo info = new ItemChangedInfo();

      info.Target = newTarget;
      info.m_modifiedProperties = m_modifiedProperties;

      return info;
    }

    private IDictionary<string, Tuple<bool, object>> m_modifiedProperties = new Dictionary<string, Tuple<bool, object>>();
  }
}
